/// \file
/// \ingroup tutorial_ProofStdVec
///
/// Selector for generic processing with stdlib collections
///
/// \macro_code
///
/// \author Gerardo Ganis (gerardo.ganis@cern.ch)

#define ProofStdVect_cxx

#include "ProofStdVect.h"
#include <TMath.h>
#include <TTree.h>
#include <TRandom3.h>
#include <TROOT.h>
#include <TString.h>
#include <TSystem.h>
#include <TFile.h>
#include <TProofOutputFile.h>
#include <TCanvas.h>
#include <TH1F.h>

//_____________________________________________________________________________
ProofStdVect::ProofStdVect()
{
   // Constructor

   fCreate = kFALSE;
   fTree = 0;
   fFile = 0;
   fProofFile = 0;
   fRandom = 0;
   fHgood = 0;
   fHbad = 0;
}

//_____________________________________________________________________________
ProofStdVect::~ProofStdVect()
{
   // Destructor

   SafeDelete(fTree);
   SafeDelete(fFile);
   SafeDelete(fRandom);
}

//_____________________________________________________________________________
void ProofStdVect::Begin(TTree * /*tree*/)
{
   // The Begin() function is called at the start of the query.
   // When running with PROOF Begin() is only called on the client.
   // The tree argument is deprecated (on PROOF 0 is passed).

   TString option = GetOption();

   // Dataset creation run?
   if (fInput && fInput->FindObject("ProofStdVect_Create")) {
      fCreate = kTRUE;
   } else if (option.Contains("create")) {
      fCreate = kTRUE;
   }
}

//_____________________________________________________________________________
void ProofStdVect::SlaveBegin(TTree * /*tree*/)
{
   // The SlaveBegin() function is called after the Begin() function.
   // When running with PROOF SlaveBegin() is called on each slave server.
   // The tree argument is deprecated (on PROOF 0 is passed).

   TString option = GetOption();

   // Dataset creation run?
   if (fInput && fInput->FindObject("ProofStdVect_Create")) {
      fCreate = kTRUE;
   } else if (option.Contains("create")) {
      fCreate = kTRUE;
   }

   // If yes, create the output file ...
   if (fCreate) {
      // Just create the object
      UInt_t opt = TProofOutputFile::kRegister | TProofOutputFile::kOverwrite | TProofOutputFile::kVerify;
      fProofFile = new TProofOutputFile("ProofStdVect.root",
                                        TProofOutputFile::kDataset, opt, "TestStdVect");

      // Open the file
      fFile = fProofFile->OpenFile("RECREATE");
      if (fFile && fFile->IsZombie()) SafeDelete(fFile);

      // Cannot continue
      if (!fFile) {
         Info("SlaveBegin", "could not create '%s': instance is invalid!", fProofFile->GetName());
         return;
      }

      // Create a TTree
      fTree = new TTree("stdvec", "Tree with std vector");
      fTree->Branch("Vb",&fVb);
      fTree->Branch("Vfx",&fVfx);
      fTree->Branch("Vfy",&fVfy);
      // File resident
      fTree->SetDirectory(fFile);
      fTree->AutoSave();

      // Init the random generator
      fRandom = new TRandom3(0);

   } else {
      // Create two histograms
      fHgood = new TH1F("Hgood", "Good hits", 100., -2.5, 2.5);
      fHbad = new TH1F("Hbad", "Bad hits", 100., -6., 6.);
      fOutput->Add(fHgood);
      fOutput->Add(fHbad);
   }
}

//_____________________________________________________________________________
Bool_t ProofStdVect::Process(Long64_t entry)
{
   // The Process() function is called for each entry in the tree (or possibly
   // keyed object in the case of PROOF) to be processed. The entry argument
   // specifies which entry in the currently loaded tree is to be processed.
   // It can be passed to either ProofStdVect::GetEntry() or TBranch::GetEntry()
   // to read either all or the required parts of the data. When processing
   // keyed objects with PROOF, the object is already loaded and is available
   // via the fObject pointer.
   //
   // This function should contain the "body" of the analysis. It can contain
   // simple or elaborate selection criteria, run algorithms on the data
   // of the event and typically fill histograms.
   //
   // The processing can be stopped by calling Abort().
   //
   // Use fStatus to set the return value of TTree::Process().
   //
   // The return value is currently not used.

   if (fCreate) {
      if (!fTree) return kTRUE;

      // Number of vectors
      Int_t nv =  (Int_t) (entry % 10);
      if (nv < 1) nv = 1;

      // Create vectors
      for (Int_t i = 0; i < nv; i++) {
         std::vector<bool> vb;
         std::vector<float> vfx, vfy;
         Int_t np =  (Int_t) (entry % 100);
         if (np < 1) np = 1;
         for (Int_t j = 0; j < np; j++) {
            float x = (float)j;
            float y = 5.*x;
            Double_t sy = (Double_t) (0.1*y);
            Double_t ym = fRandom->Gaus((Double_t)y, sy);
            Double_t c2 = TMath::Abs((ym - y) / sy);
            bool xb = (1. - TMath::Erfc(c2/TMath::Sqrt(2.)) > .95) ? 0 : 1;
            vb.push_back(xb);
            vfx.push_back(x);
            vfy.push_back(float(ym));
         }
         fVb.push_back(vb);
         fVfx.push_back(vfx);
         fVfy.push_back(vfy);
      }

      // Fill the tree
      fTree->Fill();

      // Clear the vectors
      std::vector<std::vector<bool> >::iterator ivb;
      for (ivb = fVb.begin(); ivb != fVb.end(); ivb++) {
         (*ivb).clear();
      }
      fVb.clear();
      std::vector<std::vector<float> >::iterator ivf;
      for (ivf = fVfx.begin(); ivf != fVfx.end(); ivf++) {
         (*ivf).clear();
      }
      fVfx.clear();
      for (ivf = fVfy.begin(); ivf != fVfy.end(); ivf++) {
         (*ivf).clear();
      }
      fVfy.clear();
   } else {
      // Read the entry
      GetEntry(entry);
      // Plot normalized values for bad and good hits
      for (UInt_t i = 0; i < fVfyr->size(); i++) {
         std::vector<bool> &vb = fVbr->at(i);
         std::vector<float> &vfx = fVfxr->at(i);
         std::vector<float> &vfy = fVfyr->at(i);
         for (UInt_t j = 0; j < vfy.size(); j++) {
            Double_t ny = (vfy.at(j) - 5*vfx.at(j)) / (0.1 * 5 * vfx.at(j));
            if (vb.at(j) < 0.5)
               fHbad->Fill(ny);
            else
               fHgood->Fill(ny);
         }
      }
   }

   return kTRUE;
}

//_____________________________________________________________________________
void ProofStdVect::SlaveTerminate()
{
   // The SlaveTerminate() function is called after all entries or objects
   // have been processed. When running with PROOF SlaveTerminate() is called
   // on each slave server.

   // Nothing to do in read mode
   if (!fCreate) return;

   // Write the ntuple to the file
   if (fFile) {
      if (!fTree) {
         Error("SlaveTerminate", "'tree' is undefined!");
         return;
      }
      Bool_t cleanup = kFALSE;
      TDirectory::TContext ctxt;
      if (fTree->GetEntries() > 0) {
         fFile->cd();
         fTree->Write();
         fProofFile->Print();
         fOutput->Add(fProofFile);
      } else {
         cleanup = kTRUE;
      }
      fTree->SetDirectory(0);
      fFile->Close();
      // Cleanup, if needed
      if (cleanup) {
         TUrl uf(*(fFile->GetEndpointUrl()));
         SafeDelete(fFile);
         gSystem->Unlink(uf.GetFile());
         SafeDelete(fProofFile);
      }
   }
}

//_____________________________________________________________________________
void ProofStdVect::Terminate()
{
   // The Terminate() function is the last function to be called during
   // a query. It always runs on the client, it can be used to present
   // the results graphically or save the results to file.

   // Nothing to do in create mode
   if (fCreate) return;

   // Create a canvas, with 2 pads
   TCanvas *c1 = new TCanvas("cvstdvec", "Test StdVec", 800,10,700,780);
   c1->Divide(1,2);
   TPad *pad1 = (TPad *) c1->GetPad(1);
   TPad *pad2 = (TPad *) c1->GetPad(2);
   pad2->cd();
   if (fHbad) fHbad->Draw();
   pad1->cd();
   if (fHgood) fHgood->Draw();
   c1->cd();
   c1->Update();
}

//_____________________________________________________________________________
void ProofStdVect::Init(TTree *tree)
{
   // The Init() function is called when the selector needs to initialize
   // a new tree or chain. Typically here the branch addresses and branch
   // pointers of the tree will be set.
   // It is normally not necessary to make changes to the generated
   // code, but the routine can be extended by the user if needed.
   // Init() will be called many times when running on PROOF
   // (once per file to be processed).

   // Nothing to do in create mode
   if (fCreate) return;

   // Set object pointer
   fVbr = 0;
   fVfxr = 0;
   fVfyr = 0;
   // Set branch addresses and branch pointers
   if (!tree) return;
   fChain = tree;
   fChain->SetMakeClass(1);

   fChain->SetBranchAddress("Vb", &fVbr, &b_Vb);
   fChain->SetBranchAddress("Vfx", &fVfxr, &b_Vfx);
   fChain->SetBranchAddress("Vfy", &fVfyr, &b_Vfy);
}


//_____________________________________________________________________________
Bool_t ProofStdVect::Notify()
{
   // The Notify() function is called when a new file is opened. This
   // can be either for a new TTree in a TChain or when when a new TTree
   // is started when using PROOF. It is normally not necessary to make changes
   // to the generated code, but the routine can be extended by the
   // user if needed. The return value is currently not used.

   // Nothing to do in create mode
   if (fCreate) return kTRUE;
   Info("Notify","processing file: %s",fChain->GetCurrentFile()->GetName());

   return kTRUE;
}
